package com.chunlin.testinterface;

import com.chunlin.testinterface.animal.*;
import com.chunlin.testinterface.cloneable.Animal;
import com.chunlin.testinterface.cloneable.Person;
import com.chunlin.testinterface.comparable.Student;
import com.chunlin.testinterface.divice.Computer;
import com.chunlin.testinterface.divice.KeyBoard;
import com.chunlin.testinterface.divice.Mouse;
import com.chunlin.testinterface.divice.USB;

import java.util.Arrays;

public class InterfaceTest {
    public static void test1() {
        Computer computer = new Computer();
        computer.powerOn();
        computer.useDevice(new Mouse());
        computer.useDevice(new KeyBoard());
        computer.powerOff();
    }

    //接口类型是一种引用类型，但是不能直接new接口的对象
    public static void test2() {
        // USB usb = new USB();
    }

    public static void test3() {
        // 可以直接通过接口名访问，说明是静态的
        System.out.println(USB.brand);

        // 编译报错：Error:(12, 12) java: 无法为最终变量brand分配值
        // 说明brand具有final属性
//        USB.brand = 2.0;
    }

    public static void test4() {
        Computer computer = new Computer();
        computer.powerOn();
        Mouse mouse = new Mouse();
        mouse.driveStart();
        KeyBoard keyBoard = new KeyBoard();
        keyBoard.driveStart();
        computer.powerOff();
    }

    //Java 面向对象编程中最常见的用法: 一个类继承一个父类, 同时实现多种接口
    //继承表达的含义是 is - a 语义, 而接口表达的含义是 具有某种特性
    //猫是一种动物, 具有会跑的特性
    //青蛙也是一种动物, 既能跑, 也能游泳
    //鸭子也是一种动物, 既能跑, 也能游, 还能飞
    //这样设计有什么好处呢? 时刻牢记多态的好处, 让程序员忘记类型
    // 有了接口之后, 类的使用者就不必关注具体类型,而只关注某个类是否具备某种能力
    public static void walk(IRunning running) {
        //在这个 walk 方法内部, 我们并不关注到底是哪种动物, 只要参数是会跑的, 就行
        System.out.println("我带着伙伴去散步");
        running.run();
    }

    public static void test5() {
        Cat cat = new Cat("小猫");
        walk(cat);

        Duck duck = new Duck("鸭子");
        walk(duck);

        //甚至参数可以不是 "动物", 只要会跑就行
        Robot robot = new Robot("机器人");
        walk(robot);
    }

    public static void test6() {
        //给定一个学生对象数组, 对这个对象数组中的元素进行排序(按分数降序)
        Student[] students = new Student[]{
                new Student("张三", 95),
                new Student("李四", 96),
                new Student("王五", 97),
                new Student("赵六", 92),
        };

        //按照之前的理解, 数组有一个现成的 sort 方法, 能否直接使用这个方法呢?
        //抛出异常
        //Exception in thread "main" java.lang.ClassCastException: class com.chunlin.testinterface.comparable.Student cannot be cast to class java.lang.Comparable
        //仔细思考, 不难发现, 和普通的整数不一样, 两个整数是可以直接比较的, 大小关系明确
        // 而两个学生对象的大小关系怎么确定? 需要我们额外指定
        //可以让Student 类实现 Comparable 接口, 并实现其中的 compareTo 方法
        //也就是说自定义类型的大小如何比较,可以通过实现 Comparable 接口, 并实现其中的 compareTo 方法来进行解决
        //这个可以类比为C++中的仿函数
        Arrays.sort(students);
        System.out.println(Arrays.toString(students));
    }

    //为了进一步加深对接口的理解, 可以尝试实现一个 sort 方法来完成刚才的排序过程(使用冒泡排序)
    public static void bubbleSort(Comparable[] arrays) {
        for (int bound = 0; bound < arrays.length - 1; ++bound) {
            for (int cur = 0; cur < arrays.length - 1 - bound; ++cur) {
                if (arrays[cur].compareTo(arrays[cur + 1]) > 0) {
                    Comparable temp = arrays[cur];
                    arrays[cur] = arrays[cur + 1];
                    arrays[cur + 1] = temp;
                }
            }
        }
    }

    public static void test7() {
        Student[] students = new Student[]{
                new Student("张三", 95),
                new Student("李四", 96),
                new Student("王五", 97),
                new Student("赵六", 92),
        };

        bubbleSort(students);
        System.out.println(Arrays.toString(students));
    }

    public static void test8() throws CloneNotSupportedException {
        com.chunlin.testinterface.cloneable.Animal animal = new com.chunlin.testinterface.cloneable.Animal();
        com.chunlin.testinterface.cloneable.Animal animal1 = (Animal) animal.clone();

        System.out.println(animal.name);
        System.out.println(animal1.name);

        //虽然这里对象中的内容一样,但是是两个不同的对象
        System.out.println(animal1 == animal);

        animal.name = "test";
        System.out.println(animal.name);
        System.out.println(animal1.name);
    }

    public static void test9() throws CloneNotSupportedException {
        Person person1 = new Person();
        Person person2 = (Person) person1.clone();
        System.out.println("通过person2修改前的结果");
        System.out.println(person1.money.m);
        System.out.println(person2.money.m);

        System.out.println("通过person2修改后的结果");
        person2.money.m = 13.6;
        System.out.println(person1.money.m);
        System.out.println(person2.money.m);

        //如上代码，可以看到，通过clone，只是拷贝了Person对象
        // 但是Person对象中的Money对象，并没有拷贝
        // 因为person2这个引用修改了m的值后，person1这个引用访问m的时候，值也发生了改变
        // 这里就是发生了浅拷贝
        //浅拷贝：
        //浅拷贝复制对象时，只复制对象本身及其所有成员变量的值
        //如果成员变量是引用类型（对象），那么复制的是引用，而不是引用指向的对象
        //因此，原始对象和克隆对象会共享引用类型的成员变量

        //所以需要通过深拷贝来解决问题
        //深拷贝：
        //深拷贝不仅复制对象本身，还递归地复制所有引用类型的成员变量所引用的对象
        //这样，原始对象和克隆对象就完全独立，互不影响
    }

    public static void test10() throws CloneNotSupportedException {
        Person person1 = new Person();
        Person person2 = (Person) person1.clone();
        System.out.println("通过person2修改前的结果");
        System.out.println(person1.money.m);
        System.out.println(person2.money.m);

        System.out.println("通过person2修改后的结果");
        person2.money.m = 199.9;
        System.out.println(person1.money.m);
        System.out.println(person2.money.m);

        //Java中抽象类和接口的区别
        //多继承：
        //抽象类：一个类只能继承一个抽象类
        //接口：一个类可以实现多个接口

        //关系:
        //抽象类:一个抽象类可以继承实现多个接口
        //接口:接口不能继承抽象类,但是接口可以使用extends关键字继承多个父接口

        //方法实现：
        //抽象类：可以包含抽象方法和具体方法
        //接口：Java 8之前只能包含抽象方法；Java 8之后可以包含默认方法和静态方法

        //成员变量：
        //抽象类：可以有普通字段。
        //接口：只能有常量（public static final）

        //构造函数：
        //抽象类：可以有构造函数。
        //接口：没有构造函数

        //访问修饰符：
        //抽象类：可以有各种访问修饰符（public, protected, private）
        //接口：默认方法和抽象方法默认是 public，不能是 protected 或 private；静态方法默认也是 public

        //子类使用:
        //抽象类:使用extends关键字继承抽象类
        //接口:使用implements关键字
    }

    public static void test11() {
    }

    public static void test12() {
    }

    public static void main(String[] args) throws CloneNotSupportedException {
//        test1();
//        test3();
//        test4();
//        test5();
//        test6();
//        test7();
//        test8();
//        test9();
        test10();
    }
}
